package com.jwd.avmanager.server;

import com.alibaba.fastjson.JSON;
import com.jwd.avmanager.constant.Cmd;
import com.jwd.avmanager.entity.DeviceInfo;
import com.jwd.avmanager.entity.SysUserinfo;
import com.jwd.avmanager.entity.WebSocketInfo;
import com.jwd.avmanager.pojo.AlterUsetInfo;
import com.jwd.avmanager.pojo.StreamsAddDate;
import com.jwd.avmanager.pojo.TerminalUnLogin;
import com.jwd.avmanager.repository.BDMybais;
import com.jwd.avmanager.util.DebugLogger;
import com.jwd.avmanager.util.ResponseSendUtil;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Description: 管理客户端连接
 * @Author: david
 * @CreateTime: 2022/4/21
 * @Company: jwd
 */
public class ClientManager {
    Logger logger = LoggerFactory.getLogger(ClientManager.class);
    //确保socket和在线成员保持一致
    private ConcurrentHashMap<String, ChannelHandlerContext> socketClients;//缓存所有的tcp连接
    private ConcurrentHashMap<String, DeviceInfo> socketMembers;//缓存所有在线的成员
    private ConcurrentHashMap<Integer, CopyOnWriteArrayList<String>> fileMatching;//设备文件缓存
    private ConcurrentHashMap<String, Integer> rtcRoom;//房间号的缓存匹配
    private ConcurrentHashMap<String, WebSocketInfo> webSocketMessage;//缓存所有在线的客户端
    private ConcurrentHashMap<String, StreamsAddDate> streamsAddDateMap;    //推流数据  -时间

    private static ClientManager instance;
//    private DBManager dbManager;


    public static ClientManager getInstance() {
        if (instance == null) {
            synchronized (ClientManager.class) {
                if (instance == null) {
                    instance = new ClientManager();
                }
            }
        }
        return instance;
    }

    public ClientManager() {
//        dbManager = new DBManager();
        socketMembers = new ConcurrentHashMap<>();
        socketClients = new ConcurrentHashMap<>();
        fileMatching = new ConcurrentHashMap<Integer, CopyOnWriteArrayList<String>>();
        rtcRoom = new ConcurrentHashMap<String, Integer>();
        webSocketMessage = new ConcurrentHashMap<>();
        streamsAddDateMap = new ConcurrentHashMap<>();
    }

    /**
     * 根据id 获取ws对象数据
     * @param ID
     * @return
     */
    public WebSocketInfo getWebSocketInfo(String ID) {
        return webSocketMessage.get(ID);
    }

    /**
     * 根据id 更新ws对象数据
     * @param ID
     * @param webSocketInfo
     */

    public void updateWebSocketInfo(String ID, WebSocketInfo webSocketInfo) {
         webSocketMessage.put(ID,webSocketInfo);
    }
    /**
     * 根据设备id获取ws对象数据
     */
    public  List<WebSocketInfo> getWebSocketInfoByFileDevId(String fileDevId) {
        List<WebSocketInfo> webSocketInfos = new ArrayList<>();
        for (WebSocketInfo info : webSocketMessage.values()) {
            if (fileDevId.equals(info.getFileDevId())) {
                webSocketInfos.add(info);
            }
        }
        return webSocketInfos;
    }

    /**
     * 1
     * 添加socket的客户端连接
     *
     * @param
     * @param ctx
     */
    public void addSocketClient(String ID, ChannelHandlerContext ctx) {
        ChannelHandlerContext old = socketClients.get(ID);
        if (old != null && !old.equals(ctx)) {
            //之前有，并且和现在的不一样，先关闭，表示被顶号了

            //先把旧的移除
            DebugLogger.info("移除旧的");

            closeCtx(ctx);

        }
        socketClients.put(ID, ctx);
    }

    /**
     * 获取tcp连接
     *
     * @param clientId
     * @return
     */
    public ChannelHandlerContext getSocketClient(String clientId) {
//        Set<Map.Entry<String, ChannelHandlerContext>> entries = socketClients.entrySet();
//        for(Map.Entry<String, ChannelHandlerContext> entry:entries){
//            DebugLogger.debug("getSocketClient entry:"+entry.getKey());
//        }
        return socketClients.get(clientId);
    }


    /**
     * 根据id从存储设备信息
     * 只能获取到在线的用户状态（包括数据库持久化信息和临时状态信息）
     *
     * @param clientId
     * @return
     */
    public void setOnlineMemberByClientId(String clientId, DeviceInfo deviceInfo) {
        socketMembers.put(clientId, deviceInfo);
    }


    /**
     * 根据id从缓存获取对象
     * 只能获取到在线的用户状态（包括数据库持久化信息和临时状态信息）
     *
     * @param clientId
     * @return
     */
    public DeviceInfo getOnlineMemberByClientId(String clientId) {
        return socketMembers.get(clientId);
    }

//    /**
//     * 根据设备id清除所有缓存
//     *
//     * @param dvId
//     */
//    public void clearCache(String dvId) {
//        ChannelHandlerContext removeSocket = socketClients.remove(dvId);//清除socket缓存
//        if (removeSocket != null) {
//            closeCtx(removeSocket);
//        }
//    }

    /**
     * 关闭释放连接 和 清除缓存
     *
     * @param ctx
     */
    public void closeCtx(ChannelHandlerContext ctx) {

        String clientId = ClientManager.getInstance().getClientId(ctx);//缓存有设备
//        String imei = ClientManager.getInstance().getImeiByCtx(ctx);
        if (!ctx.isRemoved()) {
            ctx.close();
        }
        if (clientId != null) {
            socketClients.remove(clientId);
            socketMembers.remove(clientId);
            //TODO 通知组内所有人，有人下线了

            //通知调度台设备下线
            cancelDeviceRegister(clientId);
//            int groupId = onlineMemberByClientId.getGroupId();
            //清除文件列表缓存
            delDevFileList(Integer.valueOf(clientId));

//            0x1004

            //检查UDP

        }
    }

    //
    private void cancelDeviceRegister(String clientId) {
        String devIdx = clientId;
        DebugLogger.info(devIdx + " 离线");
        TerminalUnLogin terminalUnLogin = new TerminalUnLogin();
        terminalUnLogin.setDev_idx(Integer.valueOf(devIdx));
        terminalUnLogin.setKeepOnLine(false);
        BDMybais instance = BDMybais.getInstance();
        //instance.terminalUnLogin(terminalUnLogin);//函数 退出
        instance.terminalUnLogin2(terminalUnLogin);
        DebugLogger.info(devIdx + " 离线更新数据库");
//        EnumCacheManager.INSTANCE.remove(devIdx);
        //通知
        Map<String, Object> map = new HashMap<>();
        map.put("status", 0);
        String valueJson = JSON.toJSONString(map);
        String str = "{\"devID\":\"" + devIdx + "\",\"cmd\":\"" + Cmd.CMD_OUT_WEB_SOCKET_SEND_CELL + "\",\"value\":" + valueJson + "}";
        //查询设备对应的所有调度台
        List<SysUserinfo> parentGroup = instance.getParentGroup(Integer.valueOf(devIdx));
        //通知调度台设备离线
        ResponseSendUtil.sendWsMessageToDispatcher(parentGroup, str);

    }


    /**
     * 根据连接寻找clientId
     *
     * @param ctx
     * @return
     */
    public String getClientId(ChannelHandlerContext ctx) {
        if (socketClients.contains(ctx)) {
            for (Map.Entry<String, ChannelHandlerContext> entry : socketClients.entrySet()) {
                if (ctx.equals(entry.getValue())) {
                    return entry.getKey();
                }
            }
        }
        return null;
    }
//    ----------------------------------客户端------------------------------------------------------------

    /**
     * 缓存所有在线的客户端
     *
     * @param
     * @param webSocketInfo
     */
    public void addwebSocketMessage(String ID, WebSocketInfo webSocketInfo) {
        Map<String, Object> map = new HashMap();
        WebSocketInfo old = webSocketMessage.get(ID);
        WebSocketInfo old2 = webSocketInfo;
        //用户之前存在
        if (old != null && !old.equals(webSocketInfo)) {
            //之前有，并且和现在的不一样，先关闭，表示被顶号了
            map.put("state", 3);
            String valueJson = JSON.toJSONString(map);
            String str2 = "{\"devID\":\"" + 0 + "\",\"cmd\":\"" + Cmd.CMD_OUT_WEB_SOCKET_EXIT_LOGIN + "\",\"value\":" + valueJson + "}";
            if (old.getCtx() != null) {
                ResponseSendUtil.writeAndFlushWs(str2, old.getCtx());
                old.getCtx().close();
            }
            //先把旧的移除
            DebugLogger.info("移除旧的--" + ID + "----ctx ID--" + old.getCtx().channel().id());
            if(old.getFileDevId()!=null && !old.getFileDevId().isEmpty() ){
                //清除文件缓存
                delDevFileList(Integer.valueOf(old.getFileDevId()));
            }
            webSocketMessage.remove(ID);



        }
        DebugLogger.info("新用户数据添加-->" + ID + "----ctx ID--" + old2.getCtx().channel().id());
        webSocketMessage.put(ID, webSocketInfo);
        map.put("state", 2);
        String valueJson = JSON.toJSONString(map);
        String str2 = "{\"devID\":\"" + 0 + "\",\"cmd\":\"" + Cmd.CMD_OUT_WEB_SOCKET_LOGIN + "\",\"value\":" + valueJson + "}";
        if (old2.getCtx() != null) {
            ResponseSendUtil.writeAndFlushWs(str2, old2.getCtx());
        }
        //数据库修改数据

    }

    public void removeWebSocketMessage(String ID, int state, ChannelHandlerContext ctx) {

        WebSocketInfo old = webSocketMessage.get(ID);
        if (old != null) {
            Map<String, Object> map = new HashMap();
            map.put("state", state);
            String valueJson = JSON.toJSONString(map);
            //之前有，并且和现在的不一样，先关闭，表示被顶号了
            String str2 = "{\"devID\":\"" + 0 + "\",\"cmd\":\"" + Cmd.CMD_OUT_WEB_SOCKET_EXIT_LOGIN + "\",\"value\":" + valueJson + "}";
            if (old.getCtx() != null) {
                ResponseSendUtil.writeAndFlushWs(str2, old.getCtx());
            }

            //先把旧的移除
            DebugLogger.info("移除数据--" + ID + "----ctx ID--" + old.getCtx().channel().id());
            old.getCtx().close();
            if(old.getFileDevId()!=null && !old.getFileDevId().isEmpty() ){
                //清除文件缓存
                delDevFileList(Integer.valueOf(old.getFileDevId()));
            }
            webSocketMessage.remove(ID);
            //数据库修改数据
            AlterUsetInfo alterUsetInfo = new AlterUsetInfo();
            alterUsetInfo.setuIsLogin(0);
            alterUsetInfo.setuLoginName(ID);
            alterUsetInfo.setuLoginType(2);
            BDMybais instance = BDMybais.getInstance();
            instance.setuLoginName(alterUsetInfo);
            return;
        }
        ctx.close();
    }

    /**
     * 获取客户端的连接
     */
    public ChannelHandlerContext getWebSocket(String name) {
        WebSocketInfo webSocketInfo = webSocketMessage.get(name);
        if (webSocketInfo != null) {
            return webSocketInfo.getCtx();
        } else {
            return null;
        }
    }

    /**
     * 文件根据设备id转发给调度台
     *
     * @param devid 设备id
     * @param list 文件列表
     */
    public void setFileMatching(Integer devid, CopyOnWriteArrayList<String> list) {
        fileMatching.put(devid, list);
    }

    /**
     * 设备id获取文件列表
     *
     * @param devid
     * @return
     */
    public CopyOnWriteArrayList<String> getDevFileMatching(Integer devid) {
        return fileMatching.get(devid);
    }




    /**
     * 删除该条缓存
     *
     * @param devid 用户id
     */
        public void delDevFileList(Integer devid) {
        if (devid == null) {
            DebugLogger.err(logger,"delFileMatching  userid: "+devid);
            return;
        }
        boolean empty = fileMatching.containsKey(devid);
        if (!empty) {
            return;
        }
        fileMatching.remove(devid);
    }

//    ---------------------------------房间号操作0-----------------------------------------------------

    /**
     * 文件根据设备id转发给调度    台
     *
     * @param room   房间号
     * @param userId 调度台id
     */
    public void setRtcRoom(String room, Integer userId) {
        Integer integer = rtcRoom.get(room);
        if (integer != null && integer != 0) {
            //之前有，并且和现在的不一样，先关闭，表示被顶号了

            //先把旧的移除
            DebugLogger.info("移除旧的");
            rtcRoom.remove(room);

        }
        rtcRoom.put(room, userId);
    }

    /**
     * 根据房间号获取调度台id做转发
     *
     * @param room
     * @return
     */

    public Integer getRtcRoom(String room) {
        return rtcRoom.get(room);
    }

    /**
     * 删除该条缓存
     *
     * @param room
     */
    public void delRtcRoom(String room) {

        if (room == null) {
            return;
        }
        boolean empty = rtcRoom.containsKey(room);
        if (!empty) {
            return;
        }
        rtcRoom.remove(room);
    }

    //    ------------------------缓存推流时间------------------------------------------
    public void addstreamsAddDateMap(String id, StreamsAddDate streamsAddDate) {
        StreamsAddDate str = streamsAddDateMap.get(id);
        if (str != null) {
            //之前有，并且和现在的不一样，先关闭，表示被顶号了

            //先把旧的移除
            DebugLogger.info("移除旧的");
            streamsAddDateMap.remove(id);

        }
        streamsAddDateMap.put(id, streamsAddDate);
    }


    public StreamsAddDate getstreamsAddDateMap(String id) {
        return streamsAddDateMap.get(id);
    }

    /**
     * 删除该条缓存
     *
     * @param id
     */
    public void delstreamsAddDateMap(String id) {

        if (id == null) {
            return;
        }
        boolean empty = streamsAddDateMap.containsKey(id);
        if (!empty) {
            return;
        }
        streamsAddDateMap.remove(id);
    }

    /**
     * 个数
     */
    public Integer sizestreamsAddDateMap() {

        return streamsAddDateMap.size();
    }

    /**
     * 全部清空
     */
    public void clestreamsAddDateMap() {

        streamsAddDateMap.clear();
    }

    public ConcurrentHashMap get() {
        return streamsAddDateMap;
    }
}
